"""
设备管理 Schema 定义
用于接口请求、响应、校验及自动生成 API 文档
全部字段显式声明校验规则，统一使用 DeviceStatus 枚举
"""
from datetime import datetime
from decimal import Decimal
from typing import Optional, List, Dict, Any

from pydantic import BaseModel, Field, ConfigDict, model_validator

from app.schemas.file_schemas import FileInfoResponse
from common.base_enums import DeviceStatus, MeterPhase, MeterType, UpLinkType
from common.response import PaginatedResponse


# ========================== 地址信息 ==========================
class DeviceLocationInfo(BaseModel):
	"""设备位置信息"""
	address_id: Optional[int] = Field(None, description="地址ID")
	address_name: Optional[str] = Field(None, description="地址名称")
	full_address: Optional[str] = Field(None, description="完整地址")
	longitude: Optional[Decimal] = Field(None, description="经度")
	latitude: Optional[Decimal] = Field(None, description="纬度")
	address_type: Optional[str] = Field(None, description="地址类型")
	is_primary: Optional[bool] = Field(None, description="是否主地址")


class AddressCreateRequest(BaseModel):
	"""地址创建请求"""
	name: str = Field(..., max_length=128, description="地址名称")
	full_name: str = Field(..., max_length=512, description="完整地址")
	longitude: Decimal = Field(..., description="经度")
	latitude: Decimal = Field(..., description="纬度")
	level: int = Field(7, description="地址级别，默认为7(详细地址)")
	region_code: Optional[str] = Field(None, max_length=12, description="行政区划码")


# ========================== 台区 ==========================
class StationBase(BaseModel):
	"""台区共享字段"""
	code: Optional[str] = Field(None, max_length=16,
								description="台区编码：T+省市区编码(10位)+4位流水(共15位)，留空时由后端生成")
	serial_no: Optional[str] = Field(..., max_length=64, description="设备序列号")
	name: str = Field(..., max_length=64, description="台区名称")
	capacity_kva: Optional[int] = Field(None, ge=0, description="配变容量(kVA)")
	manager_code: Optional[str] = Field(None, max_length=32, description="负责人工号")
	status: Optional[DeviceStatus] = Field(None, description="设备状态(ONLINE-在线, OFFLINE-离线, FAULT-故障)")
	address_id: Optional[int] = Field(None, description="地址ID")


class StationCreate(StationBase):
	"""创建台区请求"""
	# 地址信息（可选）
	address_info: Optional[AddressCreateRequest] = Field(None, description="地址信息")
	# GPS坐标（用于自动创建地址）
	longitude: Optional[Decimal] = Field(None, description="经度")
	latitude: Optional[Decimal] = Field(None, description="纬度")


class StationUpdate(BaseModel):
	"""更新台区请求(局部更新)"""
	name: Optional[str] = Field(None, max_length=64, description="台区名称")
	capacity_kva: Optional[int] = Field(None, ge=0, description="配变容量(kVA)")
	manager_code: Optional[str] = Field(None, max_length=32, description="负责人工号")
	status: Optional[DeviceStatus] = Field(None, description="设备状态(ONLINE-在线, OFFLINE-离线, FAULT-故障)")
	address_id: Optional[int] = Field(None, description="地址ID")
	# 地址信息（可选）
	address_info: Optional[AddressCreateRequest] = Field(None, description="地址信息")


class StationResponse(StationBase):
	"""台区响应"""
	id: int
	created_at: datetime
	updated_at: datetime
	# 地址信息
	location_info: Optional[List[DeviceLocationInfo]] = Field(default_factory=list, description="位置信息列表")

	model_config = ConfigDict(from_attributes=True)

	@classmethod
	def from_dict_with_address(cls, data: Dict[str, Any]) -> "StationResponse":
		"""从包含地址信息的字典创建响应对象"""
		location_info = data.pop("location_info", [])
		station = cls(**data)
		station.location_info = [DeviceLocationInfo(**loc) for loc in location_info]
		return station


# ========================== 电箱 ==========================
class BoxBase(BaseModel):
	"""电箱共享字段"""
	station_id: int = Field(..., description="所属台区ID")
	code: Optional[str] = Field(None, max_length=20,
								description="电箱编码：B+台区编码(15位)+4位流水(共20位)，留空时由后端生成")
	serial_no: Optional[str] = Field(..., max_length=64, description="设备序列号")
	station_name: Optional[str] = Field(None, max_length=64, description="台区名称")
	install_date: Optional[datetime] = Field(None, description="安装日期")
	status: Optional[DeviceStatus] = Field(DeviceStatus.ONLINE,
										   description="设备状态(ONLINE-在线, OFFLINE-离线, FAULT-故障)")
	manager_code: Optional[str] = Field(None, max_length=32, description="负责人工号")
	address_id: Optional[int] = Field(None, description="地址ID")


class BoxCreate(BoxBase):
	"""创建电箱请求"""

	file_ids: Optional[List[int]] = Field(None, description="文件信息")
	address_info: Optional[AddressCreateRequest] = Field(None, description="地址信息")


class BoxUpdate(BaseModel):
	"""更新电箱请求(局部更新)"""
	station_id: Optional[int] = Field(None, description="所属台区ID")
	install_date: Optional[datetime] = Field(None, description="安装日期")
	serial_no: Optional[str] = Field(None, max_length=64, description="设备序列号")
	status: Optional[DeviceStatus] = Field(None, description="设备状态")
	manager_code: Optional[str] = Field(None, max_length=32, description="负责人工号")
	# 上级设备信息
	parent_device_info: Optional[Dict[str, Any]] = Field(None, description="上级设备信息")
	# 地址信息（通过AddressBindingService处理）
	address_id: Optional[int] = Field(None, description="地址ID")
	# 文件更新信息
	file_ids: Optional[List[int]] = Field(None, description="文件ID列表(全量替换)")
	add_file_ids: Optional[List[int]] = Field(None, description="新增文件ID列表(增量模式)")
	del_file_ids: Optional[List[int]] = Field(None, description="删除文件ID列表(增量模式)")


class BoxResponse(BoxBase):
	"""电箱响应"""
	id: int
	created_at: datetime
	updated_at: datetime
	# 地址信息
	location_info: Optional[List[DeviceLocationInfo]] = Field(default_factory=list, description="位置信息列表")
	# 文件信息
	files: Optional[List[FileInfoResponse]] = Field(default_factory=list, description="关联文件列表")

	open_status: int = Field(0, description="箱门关闭情况，0:关闭，1:开启")

	model_config = ConfigDict(from_attributes=True)

	@classmethod
	def from_dict_with_address(cls, data: Dict[str, Any]) -> "BoxResponse":
		"""从包含地址信息的字典创建响应对象"""
		location_info = data.pop("location_info", [])
		files = data.pop("files", [])
		box = cls(**data)
		box.location_info = [DeviceLocationInfo(**loc) for loc in location_info]
		box.files = [FileInfoResponse(**file) for file in files]
		return box


# ========================== RTU ==========================
class RTUBase(BaseModel):
	"""ＲＴＵ共享字段"""
	box_id: int = Field(..., description="所属电箱ID")
	box_no: Optional[str] = Field(None, max_length=64, description="电箱编号")
	code: Optional[str] = Field(None, max_length=64, description="编号，留空时由后端生成")
	serial_no: Optional[str] = Field(..., max_length=64, description="设备序列号")
	model: Optional[str] = Field(None, max_length=32, description="设备型号")
	collector_type: Optional[str] = Field(None, max_length=20, description="采集器类型(Master/RTU)")
	station_id: Optional[int] = Field(None, description="所属台区ID（冗余）")
	station_no: Optional[str] = Field(None, description="所属台区序列号（冗余）")
	station_name: Optional[str] = Field(None, description="所属台区名称（冗余）")
	comm_protocol: Optional[str] = Field(None, max_length=20, description="通信协议")
	uplink_type: Optional[UpLinkType] = Field(None, description="上行类型(4G/NB/ETH)")
	uplink_no: Optional[str] = Field(None, max_length=32, description="SIM卡号或IP地址")
	status: Optional[DeviceStatus] = Field(DeviceStatus.ONLINE,
										   description="设备状态(ONLINE-在线, OFFLINE-离线, FAULT-故障)")
	manager_code: Optional[str] = Field(None, max_length=32, description="负责人工号")
	last_online: Optional[datetime] = Field(None, description="最后在线时间")
	address_id: Optional[int] = Field(None, description="地址ID")


class RTUCreate(RTUBase):
	"""创建RTU请求"""
	file_ids: Optional[List[int]] = Field(None, description="文件信息")
	address_info: Optional[AddressCreateRequest] = Field(None, description="地址信息")


class RTUUpdate(BaseModel):
	"""更新RTU请求(局部更新)"""
	box_id: Optional[int] = Field(None, description="所属电箱ID")
	model: Optional[str] = Field(None, max_length=32, description="设备型号")
	collector_type: Optional[str] = Field(None, max_length=20, description="采集器类型(Master/RTU)")
	station_id: Optional[int] = Field(None, description="所属台区ID（冗余）")
	serial_no: Optional[str] = Field(None, max_length=64, description="设备序列号")
	comm_protocol: Optional[str] = Field(None, max_length=20, description="通信协议")
	uplink_type: Optional[str] = Field(None, max_length=10, description="上行类型")
	uplink_no: Optional[str] = Field(None, max_length=32, description="SIM卡号或IP地址")
	status: Optional[DeviceStatus] = Field(None, description="设备状态")
	manager_code: Optional[str] = Field(None, max_length=32, description="负责人工号")
	last_online: Optional[datetime] = Field(None, description="最后在线时间")
	# 上级设备信息
	parent_device_info: Optional[Dict[str, Any]] = Field(None, description="上级设备信息")
	# 地址信息（通过AddressBindingService处理）
	address_id: Optional[int] = Field(None, description="地址ID")
	# 文件更新信息
	file_ids: Optional[List[int]] = Field(None, description="文件ID列表(全量替换)")
	add_file_ids: Optional[List[int]] = Field(None, description="新增文件ID列表(增量模式)")
	del_file_ids: Optional[List[int]] = Field(None, description="删除文件ID列表(增量模式)")


class RTUResponse(RTUBase):
	"""RTU响应"""
	id: int
	created_at: datetime
	updated_at: datetime
	# 地址信息
	location_info: Optional[List[DeviceLocationInfo]] = Field(default_factory=list, description="位置信息列表")
	# 文件信息
	files: Optional[List[FileInfoResponse]] = Field(default_factory=list, description="关联文件列表")

	model_config = ConfigDict(from_attributes=True)

	@classmethod
	def from_dict_with_address(cls, data: Dict[str, Any]) -> "RTUResponse":
		"""从包含地址信息的字典创建响应对象"""
		location_info = data.pop("location_info", [])
		files = data.pop("files", [])
		rtu = cls(**data)
		rtu.location_info = [DeviceLocationInfo(**loc) for loc in location_info]
		rtu.files = [FileInfoResponse(**file) for file in files]
		return rtu


# ========================== 电表 ==========================
class MeterBase(BaseModel):
	"""电表共享字段"""
	code: Optional[str] = Field(None, max_length=24,
								description="电表编码：M+RTU编码(20位)+相别(1位)+3位流水(共25位)，留空时由后端生成")
	rtu_id: Optional[int] = Field(..., description="所属RTU ID")
	rtu_no: Optional[str] = Field(None, max_length=32, description="RTU编号")
	rtu_serial_no: Optional[str] = Field(None, max_length=64, description="电箱设备序列号")
	box_id: Optional[int] = Field(None, description="所属电箱ID")
	box_no: Optional[str] = Field(None, max_length=32, description="电箱编号")
	box_serial_no: Optional[str] = Field(None, max_length=64, description="电箱设备序列号")
	station_id: int = Field(..., description="所属台区ID")
	station_name: Optional[str] = Field(None, max_length=32, description="台区名称")
	station_serial_no: Optional[str] = Field(None, max_length=64, description="提取设备序列号")
	serial_no: Optional[str] = Field(..., max_length=64, description="设备序列号")
	meter_type: Optional[MeterType] = Field(None, description="电表类型(单相/三相)")
	phase: Optional[MeterPhase] = Field(None, description="相别(A/B/C/3P)")
	ct_ratio: int = Field(1, ge=1, description="CT变比")
	pt_ratio: int = Field(1, ge=1, description="PT变比")
	init_reading: float = Field(0.0, ge=0, description="初始读数(kWh)")
	status: Optional[DeviceStatus] = Field(DeviceStatus.ONLINE, description="设备状态")
	manager_code: Optional[str] = Field(None, max_length=64, description="负责人工号")
	commissioning_date: Optional[datetime] = Field(None, description="投运日期")
	calibration_date: Optional[datetime] = Field(None, description="最近检定日期")
	calibration_due_date: Optional[datetime] = Field(None, description="下次检定日期")
	address_id: Optional[int] = Field(None, description="地址ID")

	@model_validator(mode="after")
	def _check_combo(self) -> "MeterBase":
		mt = (self.meter_type.value or "")
		ph = (self.phase.value or "")

		# ① 取值范围校验
		if mt and mt not in MeterType.db_values():
			raise ValueError(f"meter_type 只能为 SINGLE 或 THREE_PHASE，当前值: {mt}")

		return self


class MeterCreate(MeterBase):
	"""创建电表请求"""
	# 地址信息(可选,用于创建新地址)
	file_ids: Optional[List[int]] = Field(None, description="文件信息")
	address_info: Optional[AddressCreateRequest] = Field(None, description="地址信息")
	# 层级关系字段
	parent_meter_id: Optional[int] = Field(None, description="上级三相表ID(仅单相表需要)")


class MeterUpdate(BaseModel):
	"""更新电表请求(局部更新)"""
	rtu_id: Optional[int] = Field(None, description="所属RTU ID")
	box_id: Optional[int] = Field(None, description="所属BOX ID")
	serial_no: Optional[str] = Field(None, min_length=1, max_length=64, description="设备序列号")
	meter_type: Optional[str] = Field(None, max_length=20, description="电表类型")
	phase: Optional[str] = Field(None, max_length=10, description="相别")
	station_id: Optional[int] = Field(None, description="所属台区ID(冗余)")
	ct_ratio: Optional[int] = Field(None, ge=1, description="CT变比")
	pt_ratio: Optional[int] = Field(None, ge=1, description="PT变比")
	init_reading: Optional[float] = Field(None, ge=0, description="初始读数(kWh)")
	status: Optional[DeviceStatus] = Field(None, description="设备状态")
	manager_code: Optional[str] = Field(None, max_length=64, description="负责人工号")
	commissioning_date: Optional[datetime] = Field(None, description="投运日期")
	calibration_date: Optional[datetime] = Field(None, description="最近检定日期")
	calibration_due_date: Optional[datetime] = Field(None, description="下次检定日期")
	# 上级设备信息
	parent_device_info: Optional[Dict[str, Any]] = Field(None, description="上级设备信息")
	# 地址信息(通过AddressBindingService处理)
	address_id: Optional[int] = Field(None, description="地址ID")
	# 文件更新信息
	file_ids: Optional[List[int]] = Field(None, description="文件ID列表(全量替换)")
	add_file_ids: Optional[List[int]] = Field(None, description="新增文件ID列表(增量模式)")
	del_file_ids: Optional[List[int]] = Field(None, description="删除文件ID列表(增量模式)")
	# 层级关系字段
	parent_meter_id: Optional[int] = Field(None, description="上级三相表ID(仅单相表需要)")


class MeterResponse(MeterBase):
	"""电表响应"""
	id: int
	created_at: datetime
	updated_at: datetime
	# 地址信息
	location_info: Optional[List[DeviceLocationInfo]] = Field(default_factory=list, description="位置信息列表")
	# 文件信息
	files: Optional[List[FileInfoResponse]] = Field(default_factory=list, description="关联文件列表")

	model_config = ConfigDict(from_attributes=True)

	@classmethod
	def from_dict_with_address(cls, data: Dict[str, Any]) -> "MeterResponse":
		"""从包含地址信息的字典创建响应对象"""
		location_info = data.pop("location_info", [])
		files = data.pop("files", [])
		meter = cls(**data)
		meter.location_info = [DeviceLocationInfo(**loc) for loc in location_info]
		meter.files = [FileInfoResponse(**file) for file in files]
		return meter


# ========================== 设备事件 ==========================
class DeviceEventBase(BaseModel):
	"""设备事件共享字段"""
	target_type: str = Field(..., max_length=20, description="目标类型(STATION/BOX/RTU/METER)")
	target_id: int = Field(..., description="目标设备ID")
	event_type: str = Field(..., max_length=20, description="事件类型(REPLACED/DECOMMISSIONED/FAULT/RESTORED)")
	event_time: datetime = Field(..., description="事件时间(UTC)")
	operator_id: int = Field(..., description="操作人ID")
	related_device_id: Optional[int] = Field(None, description="关联设备ID")
	old_value: Optional[str] = Field(None, max_length=32, description="旧值")
	new_value: Optional[str] = Field(None, max_length=32, description="新值")
	reason: Optional[str] = Field(None, max_length=500, description="事件原因")
	photo_files: Optional[str] = Field(None, description="现场照片ID列表(逗号分隔)")
	work_order_id: Optional[int] = Field(None, description="关联工单ID")
	details: Optional[str] = Field(None, description="其他详细信息(JSON)")


class DeviceEventCreate(DeviceEventBase):
	"""创建设备事件请求"""
	pass


class DeviceEventResponse(DeviceEventBase):
	"""设备事件响应"""
	id: int
	created_at: datetime

	model_config = ConfigDict(from_attributes=True)


# ========================== 设备操作日志 ==========================
class DeviceOperationLogBase(BaseModel):
	"""设备操作日志共享字段"""
	target_type: str = Field(..., max_length=20, description="目标类型")
	target_id: int = Field(..., description="目标设备ID")
	op_type: str = Field(..., max_length=20, description="操作类型(CREATE/UPDATE/DELETE)")
	op_by: int = Field(..., description="操作人ID")
	note: Optional[str] = Field(None, max_length=500, description="操作备注")
	old_vals: Optional[str] = Field(None, description="变更前值(JSON)")
	new_vals: Optional[str] = Field(None, description="变更后值(JSON)")


class DeviceOperationLogCreate(DeviceOperationLogBase):
	"""创建操作日志请求"""
	pass


class DeviceOperationLogResponse(DeviceOperationLogBase):
	"""操作日志响应"""
	id: int
	op_at: datetime

	model_config = ConfigDict(from_attributes=True)


# ========================== 查询参数 ==========================
class DeviceQuery(BaseModel):
	"""设备通用查询参数"""
	page: int = Field(1, ge=1, description="页码")
	size: int = Field(20, ge=1, le=1000, description="每页条数")
	keyword: Optional[str] = Field(None, max_length=64, description="关键字(编码/名称/序列号)")
	status: Optional[DeviceStatus] = Field(None, description="设备状态筛选")
	manager_code: Optional[str] = Field(None, max_length=64, description="负责人编号")
	province_code: Optional[str] = Field(None, max_length=6, description="省编码")
	city_code: Optional[str] = Field(None, max_length=6, description="市编码")
	area_code: Optional[str] = Field(None, max_length=6, description="区编码")
	station_id: Optional[int] = Field(None, description="台区ID")
	box_id: Optional[int] = Field(None, description="电箱ID")
	rtu_id: Optional[int] = Field(None, description="RUTID")
	phase: Optional[MeterPhase] = Field(None, description="相别(A/B/C/3P)")


# ========================== 地址选择响应 ==========================
class DeviceAddressOption(BaseModel):
	"""设备地址选项"""
	id: int = Field(..., description="地址ID")
	name: str = Field(..., description="地址名称")
	full_name: str = Field(..., description="完整地址")
	level: int = Field(..., description="地址级别")
	region_code: Optional[str] = Field(None, description="行政区划码")
	longitude: Optional[Decimal] = Field(None, description="经度")
	latitude: Optional[Decimal] = Field(None, description="纬度")
	parent_id: Optional[int] = Field(None, description="父级地址ID")


# ========================== 设备操作响应 ==========================
class DeviceOperationResult(BaseModel):
	"""设备操作结果"""
	success: bool = Field(..., description="操作是否成功")
	message: str = Field(..., description="操作消息")
	device_id: Optional[int] = Field(None, description="设备ID")
	old_device_id: Optional[int] = Field(None, description="旧设备ID（替换场景）")
	new_device_id: Optional[int] = Field(None, description="新设备ID（替换场景）")


# ========================== 批量操作结果 ==========================
class BatchOperationResult(BaseModel):
	"""批量操作结果"""
	total: int = Field(..., description="总数")
	success_count: int = Field(..., description="成功数量")
	failed_count: int = Field(..., description="失败数量")
	errors: List[str] = Field(default_factory=list, description="错误信息列表")


# ========================== 设备序列号验证 ==========================
class SerialNoValidationRequest(BaseModel):
	"""设备序列号验证请求"""
	device_type: str = Field(..., description="设备类型(STATION/BOX/RTU/METER)")
	serial_no: str = Field(..., min_length=1, max_length=64, description="设备序列号")
	filter_status: bool = Field(True, description="是否过滤设备状态（不返回已停用的设备）")


class SerialNoValidationResponse(BaseModel):
	"""设备序列号验证响应"""
	exists: bool = Field(..., description="序列号是否已存在")
	device_id: Optional[int] = Field(None, description="设备ID（若存在）")
	device_type: Optional[str] = Field(None, description="设备类型")
	device_code: Optional[str] = Field(None, description="设备编码")
	device_name: Optional[str] = Field(None, description="设备名称（仅台区有）")
	status: Optional[str] = Field(None, description="设备状态")
	location_info: Optional[str] = Field(None, description="位置信息简述")


# ========================== 设备树节点 ==========================
class DeviceTreeNode(BaseModel):
	"""设备树节点"""
	id: int = Field(..., description="设备ID")
	name: str = Field(..., description="设备名称")
	code: str = Field(..., description="设备编码")
	device_type: str = Field(..., description="设备类型")
	status: Optional[str] = Field(None, description="设备状态")
	children: List["DeviceTreeNode"] = Field(default_factory=list, description="子设备列表")
	device_id: Optional[int] = Field(None, description="设备ID")
	old_device_id: Optional[int] = Field(None, description="旧设备ID（替换操作）")
	new_device_id: Optional[int] = Field(None, description="新设备ID（替换操作）")


class BatchOperationResult(BaseModel):
	"""批量操作结果"""
	total: int = Field(..., description="总数")
	success_count: int = Field(..., description="成功数量")
	failed_count: int = Field(..., description="失败数量")
	errors: List[str] = Field(default_factory=list, description="错误信息列表")


class DeviceTreeNode(BaseModel):
	"""设备树节点"""
	id: int = Field(..., description="设备ID")
	code: str = Field(..., description="设备编码")
	serial_no: Optional[str] = Field(None, description="设备序列号")
	manager_code: Optional[str] = Field(None, description="负责人编号")
	name: Optional[str] = Field(None, description="设备名称")
	device_type: str = Field(..., description="设备类型")
	phase: Optional[MeterPhase] = Field(None, description="相别(A/B/C/3P)")
	status: Optional[DeviceStatus] = Field(None, description="设备状态")
	# 地址信息
	location_info: Optional[List[DeviceLocationInfo]] = Field(default_factory=list, description="位置信息列表")
	children: List["DeviceTreeNode"] = Field(default_factory=list, description="子设备列表")

	model_config = ConfigDict(from_attributes=True)


# 解决前向引用问题
DeviceTreeNode.model_rebuild()


# ========================== 设备事件和操作日志查询 ==========================
class DeviceEventQuery(BaseModel):
	"""设备事件查询参数"""
	page: int = Field(1, ge=1, description="页码")
	size: int = Field(20, ge=1, le=100, description="每页大小")
	keyword: Optional[str] = Field(None, description="关键字搜索(原因/详情)")
	device_id: Optional[int] = Field(None, description="设备ID")
	device_type: Optional[str] = Field(None, description="设备类型(STATION/BOX/RTU/METER)")
	event_type: Optional[str] = Field(None, description="事件类型(REPLACED/DECOMMISSIONED/FAULT/RESTORED)")
	start_time: Optional[datetime] = Field(None, description="开始时间")
	end_time: Optional[datetime] = Field(None, description="结束时间")


class DeviceOperationLogQuery(BaseModel):
	"""设备操作日志查询参数"""
	page: int = Field(1, ge=1, description="页码")
	size: int = Field(20, ge=1, le=100, description="每页大小")
	keyword: Optional[str] = Field(None, description="关键字搜索(备注/操作类型)")
	device_id: Optional[int] = Field(None, description="设备ID")
	device_type: Optional[str] = Field(None, description="设备类型(STATION/BOX/RTU/METER)")
	op_type: Optional[str] = Field(None, description="操作类型(CREATE/UPDATE/DELETE/STATUS_CHANGE)")
	start_time: Optional[datetime] = Field(None, description="开始时间")
	end_time: Optional[datetime] = Field(None, description="结束时间")


# ========================== 分页响应 ==========================
PaginatedStation = PaginatedResponse[StationResponse]
PaginatedBox = PaginatedResponse[BoxResponse]
PaginatedRTU = PaginatedResponse[RTUResponse]
PaginatedMeter = PaginatedResponse[MeterResponse]
